package com.ljz.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;

/**
 * Copyright © 2020年03月18日 liujiaze. All rights reserved.
 * @Project: javaStudy
 * @Package: com.ljz.redis
 * @Description:
 * @author: liujiaze
 * @date: 2020年03月18日 17:03
 * @version: V1.0
 */
public class RedisTransaction {
    public static void main(String[] args) {
        demonstrate3();
    }


    /**
     * 入队执行
     */
    private static void demonstrate() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        Transaction transaction = jedis.multi(); //开启事务
        //入队
        transaction.set("k4", "v4");
        transaction.set("k5", "v5");
        //执行
        transaction.exec();
    }

    /**
     * 回滚
     */
    private static void demonstrate1() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        Transaction transaction = jedis.multi(); //开启事务
        //入队
        transaction.set("k4", "v44");
        transaction.set("k5", "v55");

        //执行
        transaction.discard();
    }

    /**
     * 监控
     */
    private static void demonstrate3() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);

        //监控key，如果改动了 事务就被放弃
         /*3
         jedis.watch("serialNum");
         jedis.set("serialNum","s#####################");
         jedis.unwatch();*/

        Transaction transaction = jedis.multi();//被当作一个命令进行执行
        Response<String> response = transaction.get("serialNum");
        transaction.set("serialNum", "s002");
        response = transaction.get("serialNum");
        transaction.lpush("list3", "a");
        transaction.lpush("list3", "b");
        transaction.lpush("list3", "c");

        transaction.exec();
        //2 transaction.discard();
        System.out.println("serialNum***********" + response.get());


    }


    /**
     * 加锁
     * 通俗点讲，watch命令就是标记一个键，如果标记了一个键， 在提交事务前如果该键被别人修改过，那事务就会失败，这种情况通常可以在程序中
     * 重新再尝试一次。
     * 首先标记了键balance，然后检查余额是否足够，不足就取消标记，并不做扣减； 足够的话，就启动事务进行更新操作，
     * 如果在此期间键balance被其它人修改， 那在提交事务（执行exec）时就会报错， 程序中通常可以捕获这类错误再重新执行一次，直到成功。
     */
    private static void demonstrate2() {

        RedisTransaction test = new RedisTransaction();
        boolean result = test.transMethod();
        System.out.println("main result-------: " + result);
    }


    public boolean transMethod() {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        int balance;// 可用余额
        int debt;// 欠额
        //赋值
        jedis.set("balance", "100");
        jedis.set("debt", "0");
        int amtToSubtract = 10;// 实刷额度

        jedis.watch("balance"); //监控
        //jedis.set("balance","5");//此句不该出现，讲课方便。模拟其他程序已经修改了该条目
        balance = Integer.parseInt(jedis.get("balance"));
        if (balance < amtToSubtract) {
            jedis.unwatch();//放弃监控
            System.out.println("modify");
            return false;
        } else {
            System.out.println("***********transaction");
            Transaction transaction = jedis.multi(); //开启事务
            //入队
            transaction.decrBy("balance", amtToSubtract);
            transaction.incrBy("debt", amtToSubtract);
            //执行
            transaction.exec();
            //查询余额和欠额
            balance = Integer.parseInt(jedis.get("balance"));
            debt = Integer.parseInt(jedis.get("debt"));

            System.out.println("*******" + balance);
            System.out.println("*******" + debt);
            return true;
        }
    }
}
